#ifndef L_VERSION
#define L_VERSION   1111225
#endif

#include <llib.h>
using namespace llib;

#include "BVH.h"
#include "Scene.h"
#include "WaveSimulator.h"

#include <iostream>
#include <vector>
using namespace std;

class TurbineSimulator3 : public Simulator {
private:
    const Float             _timeDelta;
    Int32                   _timeStepId;
    const Float             _angularVelocity;
    Boolean                 _pause;

    const Float             _turbineRadius;
    const Float             _turbineXoffset;
    const Int32             _turbineFanNum;
    const Float             _turbineFanSize;
    const Int32             _turbineFanResolution;

    TriMesh                 _baseFanModel;
    vector<SceneObject>     _turbineLeft;
    vector<SceneObject>     _turbineRight;

    void initBaseFanModel(Float fanSize, Int32 fanResolution)
    {
        Int32 vertexNum = (fanResolution + 1) * (fanResolution + 2) / 2;
        Int32 faceNum = fanResolution * fanResolution;

        vector<Vec3f> vertexList(vertexNum);
        Vec3f curVertex(Float(-fanSize/SQRT3),0.f,0.f);
        Float dx = Float(fanSize/fanResolution/2*SQRT3);
        Float dy = Float(fanSize/fanResolution/2);
        Float triSize = Float(fanSize/fanResolution);
		Int32 index = 0;
        FOR(h,0,fanResolution + 1) {
			Vec3f lineStart = curVertex;
			FOR(w,0,h+1) {
				vertexList[index++] = curVertex;
				curVertex.y += triSize;
			}
			curVertex = lineStart;
			curVertex.x += dx;
			curVertex.y -= dy;
        }

		vector<Vec3i> faceVertexIndexList(faceNum);
		index = 0;
		Int32 upId = 0, lowId = 1;
		Vec3i tmp;
		FOR(h,0,fanResolution) {
			FOR(w,0,h+1) {
				tmp = Vec3i(upId, lowId, lowId+1);
				faceVertexIndexList[index++] = tmp;
				++upId;
				++lowId;
			}
			lowId += 1;
		}
		upId = 1;
		lowId = 4;
		FOR(h,1,fanResolution) {
			FOR(w,0,h) {
				tmp = Vec3i(upId, lowId, upId+1);
				faceVertexIndexList[index++] = tmp;
				++upId;
				++lowId;
			}
			upId += 1;
			lowId += 2;
		}

		// vertex and face-vertex index ready, build model
		_baseFanModel.SetModelData(vertexList,faceVertexIndexList);
    }

    void updateTurbineModel()
    {
        Float fanAngle = Float(2 * PI / _turbineFanNum);
        Float curAngle = Float(_angularVelocity * _timeDelta * _timeStepId);
        // build left turbine
        for(Int32 F = 0; F < _turbineFanNum; ++F, curAngle += fanAngle) {
            // build fan using origin as center
            Float cosVal = cosf(curAngle);
            Float sinVal = sinf(curAngle);
            FOR(v,0,_turbineLeft[F].Mesh.GetVertexNum()) {
                Vec3f& vertex = _turbineLeft[F].Mesh.GetVertex(v);
                Float baseX = _baseFanModel.GetVertex(v).x;
                vertex.x = baseX * cosVal;
                vertex.z = baseX * sinVal;
            }
            // move fan on x axis to final position
            FOR(v,0,_turbineLeft[F].Mesh.GetVertexNum()) {
                _turbineLeft[F].Mesh.GetVertex(v).x -= _turbineXoffset;
            }
        }
        // build right turbine as XOZ plane mirror with left
        for(Int32 F = 0; F < _turbineFanNum; ++F) {
            _turbineRight[F] = _turbineLeft[F];
            FOR(v,0,_turbineRight[F].Mesh.GetVertexNum()) {
                Vec3f& vertex = _turbineRight[F].Mesh.GetVertex(v);
                vertex.x = -vertex.x;
            }
        }
    }

public:
    TurbineSimulator3() :
      _timeDelta            ( 0.05f ),
      _timeStepId           ( 0 ),
      _angularVelocity      ( 0.3f ),
      _pause                ( FALSE ),
      _turbineRadius        ( 5.f ),
      _turbineXoffset       ( 5.f ),
      _turbineFanNum        ( 16 ),
      _turbineFanSize       ( 2.f ),
      _turbineFanResolution ( 32 )
    {
        initBaseFanModel(_turbineFanSize, _turbineFanResolution);
        // move fan forward on x axis
        FOR(v,0,_baseFanModel.GetVertexNum()) {
            _baseFanModel.GetVertex(v).x += _turbineRadius;
        }

        // init left & right turbine model vector
        _turbineLeft = vector<SceneObject>(_turbineFanNum);
        _turbineRight = vector<SceneObject>(_turbineFanNum);
        FOR(I,0,_turbineFanNum) {
            _turbineLeft[I].Init(_baseFanModel, SceneObject::Solid);
            _turbineRight[I].Init(_baseFanModel, SceneObject::Solid);
        }
      }

    virtual Boolean Render()
    {
        const static Vec3f colorCollisionFree = Vec3f(0.2f,1.f,0.2f);
        const static Vec3f colorCollision = Vec3f(1.f,0.2f,0.2f);

        if(!_pause) {
            updateTurbineModel();

            FOR(I,0,_turbineFanNum) {
                _turbineLeft[I].Fit();
                _turbineRight[I].Fit();
            }

            FOR(l,0,_turbineFanNum) {
                SceneObject& leftFan = _turbineLeft[l];
                FOR(r,0,_turbineFanNum) {
                    SceneObject& rightFan = _turbineRight[r];
                    leftFan.CollisionCheck(rightFan);
                }
            }
        }

        // draw left turbine
        glBegin(GL_TRIANGLES);
        FOR(t,0,SZ(_turbineLeft)) {
            TriMesh& turbineModel = _turbineLeft[t].Mesh;
            vector<Boolean>& collisionFlag = _turbineLeft[t].CollisionFlag;
            FOR(f,0,turbineModel.GetFaceNum()) {
                if(!collisionFlag[f]) {
                    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colorCollisionFree.cell);
                    glColor3fv(colorCollisionFree.cell);
                } else {
                    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colorCollision.cell);
                    glColor3fv(colorCollision.cell);
                }

                ModelFace& face = turbineModel.GetFace(f);
                glVertex3fv(face.GetVertex(0).cell);
                glVertex3fv(face.GetVertex(1).cell);
                glVertex3fv(face.GetVertex(2).cell);
            }
        }
        glEnd();

        // draw right turbine
        glBegin(GL_TRIANGLES);
        FOR(t,0,SZ(_turbineLeft)) {
            TriMesh& turbineModel = _turbineRight[t].Mesh;
            vector<Boolean>& collisionFlag = _turbineRight[t].CollisionFlag;
            FOR(f,0,turbineModel.GetFaceNum()) {
                if(!collisionFlag[f]) {
                    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colorCollisionFree.cell);
                    glColor3fv(colorCollisionFree.cell);
                } else {
                    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colorCollision.cell);
                    glColor3fv(colorCollision.cell);
                }

                ModelFace& face = turbineModel.GetFace(f);
                glVertex3fv(face.GetVertex(0).cell);
                glVertex3fv(face.GetVertex(1).cell);
                glVertex3fv(face.GetVertex(2).cell);
            }
        }
        glEnd();

        if(!_pause) ++_timeStepId;

        return TRUE;
    }


    virtual Boolean OnKeyDown(Uint8 key, Int32 x, Int32 y, Uint8 modifiers)
    {
        if(key == 32) { /*space*/
            _pause = !_pause;
        }
        return TRUE;
    }
};


Int32 main(Int32 argc, Char **argv)
{
    //AppFramework.SetSimulator(new Navigator(new TurbineSimulator3()));
    Navigator* nav = new Navigator(new WaveSimulator());
    nav->SetEyePosition(Vec3f(0.f,0.5f,2.f));
    AppFramework.SetSimulator(nav);
    AppFramework.SetWindowTitle("Turbine Simulator 3");
    AppFramework.SetFullScreen(FALSE);
    AppFramework.FrameworkMain(argc, argv);
    return 0;
}
